D:\a\scloud-dns\scloud-dns\src\dns\packet\mod.rs
Line | Count | Source |
1 | | use crate::dns::packet::additional::AdditionalSection; |
2 | | use crate::dns::packet::answer::AnswerSection; |
3 | | use crate::dns::packet::authority::AuthoritySection; |
4 | | use crate::dns::packet::header::Header; |
5 | | use crate::dns::packet::question::QuestionSection; |
6 | | use crate::exceptions::SCloudException; |
7 | | use rand::random; |
8 | | |
9 | | pub(crate) mod additional; |
10 | | pub(crate) mod answer; |
11 | | pub(crate) mod authority; |
12 | | pub(crate) mod header; |
13 | | pub(crate) mod question; |
14 | | |
15 | | #[derive(Debug, PartialEq)] |
16 | | pub struct DNSPacket { |
17 | | pub header: Header, |
18 | | pub questions: Vec<QuestionSection>, |
19 | | pub answers: Vec<AnswerSection>, |
20 | | pub authorities: Vec<AuthoritySection>, |
21 | | pub additionals: Vec<AdditionalSection>, |
22 | | } |
23 | | |
24 | | impl DNSPacket { |
25 | | /// Deserialize the DNS packet from a byte array |
26 | | /// # Exemple : |
27 | | /// ``` |
28 | | /// // Raw DNS query for `github.com` with type A |
29 | | /// let raw_packet: Vec<u8> = vec![ |
30 | | /// 0x12, 0x34, // ID |
31 | | /// 0x01, 0x00, // Flags (standard query, RD = 1) |
32 | | /// 0x00, 0x01, // QDCOUNT |
33 | | /// 0x00, 0x00, // ANCOUNT |
34 | | /// 0x00, 0x00, // NSCOUNT |
35 | | /// 0x00, 0x00, // ARCOUNT |
36 | | /// 0x06, b'g', b'i', b't', b'h', b'u', b'b', |
37 | | /// 0x03, b'c', b'o', b'm', |
38 | | /// 0x00, // End of QNAME |
39 | | /// 0x00, 0x01, // QTYPE = A |
40 | | /// 0x00, 0x01, // QCLASS = IN |
41 | | /// ]; |
42 | | /// |
43 | | /// let packet = DNSPacket::from_bytes(&raw_packet).unwrap(); |
44 | | /// |
45 | | /// assert_eq!(packet.header.qdcount, 1); |
46 | | /// assert_eq!(packet.questions[0].q_name, "github.com"); |
47 | | /// assert!(packet.answers.is_empty()); |
48 | | /// ``` |
49 | 2 | pub fn from_bytes(buf: &[u8]) -> Result<DNSPacket, SCloudException> { |
50 | 2 | let mut pos = 0; |
51 | | |
52 | 2 | let header = Header::from_bytes(&buf[pos..])?0 ; |
53 | 2 | pos += Header::DNS_HEADER_LEN; |
54 | | |
55 | 2 | let mut questions = Vec::new(); |
56 | 2 | for _ in 0..header.qdcount { |
57 | 2 | let (q, consumed) = QuestionSection::from_bytes(&buf, pos)?0 ; |
58 | 2 | pos += consumed; |
59 | 2 | questions.push(q); |
60 | | } |
61 | | |
62 | 2 | let mut answers = Vec::new(); |
63 | 2 | for _ in 0..header.ancount { |
64 | 1 | let (ans, consumed) = AnswerSection::from_bytes(&buf, pos)?0 ; |
65 | 1 | pos += consumed; |
66 | 1 | answers.push(ans); |
67 | | } |
68 | | |
69 | 2 | let mut authorities = Vec::new(); |
70 | 2 | for _ in 0..header.nscount { |
71 | 2 | let (ns, consumed) = AuthoritySection::from_bytes(buf, pos)?0 ; |
72 | 2 | pos += consumed; |
73 | 2 | authorities.push(ns); |
74 | | } |
75 | | |
76 | 2 | let mut additionals = Vec::new(); |
77 | 2 | for _ in 0..header.arcount { |
78 | 1 | let (add, consumed) = AdditionalSection::from_bytes(&buf, pos)?0 ; |
79 | 1 | pos += consumed; |
80 | 1 | additionals.push(add); |
81 | | } |
82 | | |
83 | 2 | Ok(DNSPacket { |
84 | 2 | header, |
85 | 2 | questions, |
86 | 2 | answers, |
87 | 2 | authorities, |
88 | 2 | additionals, |
89 | 2 | }) |
90 | 2 | } |
91 | | |
92 | | /// Serialize the DNS packet into a byte array |
93 | | /// # Exemple : |
94 | | /// ``` |
95 | | /// let packet = DNSPacket::new_query(&[QuestionSection { |
96 | | /// q_name: "github.com".to_string(), |
97 | | /// q_type: DNSRecordType::A, |
98 | | /// q_class: DNSClass::IN, |
99 | | /// }]); |
100 | | /// |
101 | | /// let bytes = packet.to_bytes().unwrap(); |
102 | | /// |
103 | | /// // A valid DNS packet is at least 12 bytes (header) |
104 | | /// assert!(bytes.len() >= 12); |
105 | | /// ``` |
106 | 2 | pub fn to_bytes(&self) -> Result<Vec<u8>, SCloudException> { |
107 | 2 | let mut bytes = Vec::new(); |
108 | | |
109 | 2 | if let Err(_) = self.header.to_bytes() { |
110 | 0 | return Err(SCloudException::SCLOUD_HEADER_BYTES_EMPTY); |
111 | 2 | } |
112 | 2 | bytes.extend_from_slice(&self.header.to_bytes()?0 ); |
113 | | |
114 | 2 | for q in &self.questions { |
115 | 2 | bytes.extend_from_slice(&q.to_bytes()?0 ); |
116 | | } |
117 | | |
118 | 2 | for ans1 in &self.answers { |
119 | 1 | bytes.extend_from_slice(&ans.to_bytes()?0 ); |
120 | | } |
121 | | |
122 | 2 | for auth1 in &self.authorities { |
123 | 1 | bytes.extend_from_slice(&auth.to_bytes()?0 ); |
124 | | } |
125 | | |
126 | 2 | for add1 in &self.additionals { |
127 | 1 | bytes.extend_from_slice(&add.to_bytes()?0 ); |
128 | | } |
129 | | |
130 | 2 | Ok(bytes) |
131 | 2 | } |
132 | | |
133 | | /// Receive one or more `QuestionSection`, and return a new DNSPacket |
134 | | /// # Exemple : |
135 | | /// ``` |
136 | | /// let query = DNSPacket::new_query(&[QuestionSection { |
137 | | /// q_name: "github.com".to_string(), |
138 | | /// q_type: DNSRecordType::A, |
139 | | /// q_class: DNSClass::IN, |
140 | | /// }]) |
141 | | /// ``` |
142 | | /// |
143 | | /// # Return : |
144 | | /// ``` |
145 | | /// DNSPacket { |
146 | | /// header: Header { |
147 | | /// id: {random_generated_id}, |
148 | | /// qr: false, |
149 | | /// opcode: 0, |
150 | | /// aa: false, |
151 | | /// tc: false, |
152 | | /// rd: true, |
153 | | /// ra: false, |
154 | | /// z: 0, |
155 | | /// rcode: 0, |
156 | | /// qdcount: 1, |
157 | | /// ancount: 0, |
158 | | /// nscount: 0, |
159 | | /// arcount: 0, |
160 | | /// }, |
161 | | /// questions: vec![QuestionSection { |
162 | | /// q_name: "github.com".to_string(), |
163 | | /// q_type: DNSRecordType::A, |
164 | | /// q_class: DNSClass::IN, |
165 | | /// }], |
166 | | /// answers: vec![], |
167 | | /// authorities: vec![], |
168 | | /// additionals: vec![], |
169 | | /// }; |
170 | | /// ``` |
171 | 5 | pub fn new_query(question_section: &[QuestionSection]) -> DNSPacket { |
172 | 5 | DNSPacket { |
173 | 5 | header: Header { |
174 | 5 | id: random::<u16>(), |
175 | 5 | qr: false, |
176 | 5 | opcode: 0, |
177 | 5 | aa: false, |
178 | 5 | tc: false, |
179 | 5 | rd: true, |
180 | 5 | ra: false, |
181 | 5 | z: 0, |
182 | 5 | rcode: 0, |
183 | 5 | qdcount: question_section.len() as u16, |
184 | 5 | ancount: 0, |
185 | 5 | nscount: 0, |
186 | 5 | arcount: 0, |
187 | 5 | }, |
188 | 5 | questions: question_section.to_vec(), |
189 | 5 | answers: vec![], |
190 | 5 | authorities: vec![], |
191 | 5 | additionals: vec![], |
192 | 5 | } |
193 | 5 | } |
194 | | } |